import gtk
from Bases import BaseObject
from devicetree import DeviceTree
from bases import widgets, listmodel

class EditorBase(BaseObject):
    def __init__(self, **kwargs):
        super(EditorBase, self).__init__(**kwargs)
        self.child_editors = {}
        self.MainController = kwargs.get('MainController')
        self.DeviceSystem = self.MainController.DeviceSystem
        frame = widgets.Frame(label=self.__class__.topwidget_label)
        self.topwidget = kwargs.get('topwidget', frame)
        self.init_editor(**kwargs)
        self.topwidget.show_all()
    def make_child_editor(self, cls, name, **kwargs):
        kwargs.setdefault('MainController', self.MainController)
        obj = cls(**kwargs)
        self.child_editors.update({name:obj})
        return obj
    def unlink(self):
        for child in self.child_editors.itervalues():
            child.unlink()
        
class DeviceEditor(EditorBase):
    topwidget_label = 'Device'
    def init_editor(self, **kwargs):
        self.device = kwargs.get('device')
        self.attribute_editor = None
        
        self.txtObj = {}
        keys = ['Name', 'Manufacturer', 'Description', 'Personality_Mode']
        for key in keys:
            obj = widgets.Text(name=key, src_object=self.device, src_attr=key.lower(),
                               allow_obj_setattr=True)
            self.topwidget.pack_start(obj.topwidget)
            self.txtObj.update({key:obj})
        #self.txtName = widgets.Text(label='Name', src_object=self.device, 
        #                            src_attr='name', allow_obj_setattr=True)
        #self.topwidget.pack_start(self.txtName.topwidget)
        self.sbChan = widgets.SpinBtn(label='Channel Count', src_object=self.device, src_attr='channel_count', 
                                      value_max=512)
        self.topwidget.pack_start(self.sbChan.topwidget)
        
        obj = self.make_child_editor(CategoryEditor, 'categories', src_object=self.device)
        self.topwidget.pack_start(obj.topwidget)
        
        box = widgets.HBox()
        
        self.column_attr_map = {'device':{0:'id', 1:'name', 2:'chan_index'}, 
                                'global':{0:'id', 1:'name', 2:'name'}}
        names = ['id', 'Name', 'Type']
        types = [str, str, str]
        order = [1, 2]
        
        self.global_attribute_list = widgets.TreeList(name='Global Attributes', column_names=names, 
                                                      list_types=types, column_order=order)
        self.update_global_attribute_list()
        self.global_attribute_list.connect('selection_changed', self.on_global_attribute_list_selection_changed)
        self.btn_add_attribute = widgets.Button(label='Add Attribute')
        self.btn_add_attribute.connect('clicked', self.on_btn_add_attribute_clicked)
        self.global_attribute_list.topwidget.pack_start(self.btn_add_attribute)
        box.pack_start(self.global_attribute_list.topwidget, expand=True)
        
        names = ['id', 'Name', 'Channel']
        types = [str, str, int]
        order = [1, 2]
        editable = [1, 2]
        
        self.device_attribute_list = widgets.TreeList(name='Device Attributes', column_names=names, 
                                               list_types=types, column_order=order, columns_editable=editable)
        self.update_device_attribute_list()
        self.device_attribute_list.connect('selection_changed', self.on_device_attribute_list_selection_changed)
        self.device_attribute_list.connect('cell_edited', self.on_device_attribute_list_cell_edited)
        box.pack_start(self.device_attribute_list.topwidget, expand=True)
        
        self.topwidget.pack_start(box, expand=True)
        self.hbox = box
        
        self.device.connect('chan_update', self.on_device_chan_update)
        self.device.connect('attr_watch_channel_count', self.sbChan.on_object_value_changed)
        
    def unlink(self):
        super(DeviceEditor, self).unlink()
        self.device.disconnect(callback=self.on_device_chan_update)
        self.device.disconnect(callback=self.sbChan.on_object_value_changed)
    
    def update_global_attribute_list(self, key=None):
        if key is not None:
            obj = self.DeviceSystem.GlobalAttributes[key]
            values = [getattr(obj, self.column_attr_map['global'][x]) for x in [1, 2]]
            self.global_attribute_list.update({key:values})
        else:
            for key, val in self.DeviceSystem.GlobalAttributes.iteritems():
                values = [getattr(val, self.column_attr_map['global'][x]) for x in [1, 2]]
                self.global_attribute_list.update({key:values})
                
    def update_device_attribute_list(self, key=None):
        if key is not None:
            obj = self.device.Attributes[key]
            values = [getattr(obj, self.column_attr_map['device'][x]) for x in [1, 2]]
            self.device_attribute_list.update({key:values})
        else:
            for key, val in self.device.Attributes.iteritems():
                values = [getattr(val, self.column_attr_map['device'][x]) for x in [1, 2]]
                self.device_attribute_list.update({key:values})
                
    def on_device_attribute_list_cell_edited(self, **kwargs):
        key = kwargs.get('key')
        column = kwargs.get('column')
        value = kwargs.get('value')
        attr = self.column_attr_map['device'][column]
        setattr(self.device.Attributes[key], attr, value)
                
    def on_global_attribute_list_selection_changed(self, **kwargs):
        pass
        
    def on_device_attribute_list_selection_changed(self, **kwargs):
        key = kwargs.get('key')
        if self.attribute_editor is not None:
            self.attribute_editor.unlink()
            self.hbox.remove(self.attribute_editor.topwidget)
        attribute = self.device.Attributes[key]
        obj = self.make_child_editor(AttributeEditor, 'attribute', attribute=attribute)
        self.hbox.pack_start(obj.topwidget, expand=True)
        self.attribute_editor = obj
        
    def on_btn_add_attribute_clicked(self, btn):
        key = self.global_attribute_list.current_selection
        attr = self.DeviceSystem.GlobalAttributes[key]
        if attr is not None:
            dev_attr = self.device.add_attribute(attribute=attr)
            self.update_device_attribute_list(dev_attr.id)
            
    def on_device_chan_update(self, **kwargs):
        attribute = kwargs.get('attribute')
        self.update_device_attribute_list(attribute.id)
    
class PatchEditor(EditorBase):
    topwidget_label = 'Patch Editor'
    def init_editor(self, **kwargs):
        self.definition = kwargs.get('definition')
        self.patch_grid = None
        self._start_chan = 1
        self._patch_count = 1
        hbox = widgets.HBox()
        
        self.universe_combo = widgets.Combo(name='Universes', list_types=[str, str])
        self.universe_combo.connect('selection_changed', self.on_universe_combo_selection_changed)
        for key, val in self.DeviceSystem.universes.iteritems():
            self.universe_combo.update({key:val.name})
        keys = self.DeviceSystem.universes.keys()
        if len(keys) == 0:
            univ_id = None
        else:
            univ_id = keys[0]
        self.on_universe_combo_selection_changed(key=univ_id)
        self.universe_combo.set_current_selection(key=univ_id)
        hbox.pack_start(self.universe_combo.topwidget)
        
        self.sbStartChan = widgets.SpinBtn(label='Start Channel', src_object=self, src_attr='start_chan', 
                                           allow_obj_setattr=True)
        hbox.pack_start(self.sbStartChan.topwidget)
        self.sbPatchCount = widgets.SpinBtn(label='Device Count', src_object=self, src_attr='patch_count', 
                                            allow_obj_setattr=True)
        hbox.pack_start(self.sbPatchCount.topwidget)
        
        self.topwidget.pack_start(hbox)
        
        self.btn_patch = widgets.Button(label='Patch')
        self.btn_patch.connect('clicked', self.on_btn_patch_clicked)
        self.topwidget.pack_start(self.btn_patch)
        
        self.topwidget.show_all()
        
    @property
    def start_chan(self):
        return self._start_chan
    @start_chan.setter
    def start_chan(self, value):
        self._start_chan = value
        if hasattr(self, 'sbChan'):
            self.sbChan.on_object_value_changed()
        
    @property
    def patch_count(self):
        return self._patch_count
    @patch_count.setter
    def patch_count(self, value):
        self._patch_count = value
        if hasattr(self, 'sbPatchCount'):
            self.sbPatchCount.on_object_value_changed()
            
    def on_universe_combo_selection_changed(self, **kwargs):
        key = kwargs.get('key')
        print 'universe:', key
        if self.patch_grid is not None:
            self.patch_grid.unlink()
            self.topwidget.remove(self.patch_grid.topwidget)
        universe = self.DeviceSystem.universes[key]
        self.patch_grid = self.make_child_editor(UniversePatchEditor, 'patch_grid', universe=universe)
        self.topwidget.pack_start(self.patch_grid.topwidget)
    
    def on_btn_patch_clicked(self, btn):
        univ_id = self.universe_combo.current_selection
        if univ_id is not None:
            device = self.DeviceSystem.patch_device(self.definition, 
                                                    universe_id=univ_id, 
                                                    start_chan=self.start_chan, 
                                                    count=self.patch_count)
        
class AttributeEditor(EditorBase):
    topwidget_label = 'Attribute'
    def init_editor(self, **kwargs):
        self.attribute = kwargs.get('attribute')
        
        self.txtName = widgets.Entry(label='Name', src_object=self.attribute, src_attr='name')
        #self.txtName = widgets.Text(label='Name', src_object=self.attribute, 
        #                            src_attr='name', allow_obj_setattr=True)
        self.topwidget.pack_start(self.txtName.topwidget)
        
        if self.attribute.saved_class_name in ['DefinitionAttribute', 'PatchedAttribute']:
            self.sbChan = widgets.SpinBtn(label='Channel', src_object=self.attribute, src_attr='chan_index', 
                                          value_max=512)
            self.topwidget.pack_start(self.sbChan.topwidget)
            self.attribute.connect('chan_index_changed', self.sbChan.on_object_value_changed)
        else:
            self.sbChan = None
        
        obj = self.make_child_editor(CategoryEditor, 'categories', src_object=self.attribute)
        self.topwidget.pack_start(obj.topwidget)
        
        obj = self.make_child_editor(ParameterEditor, 'parameters', attribute=self.attribute)
        self.topwidget.pack_start(obj.topwidget)
        
        self.attribute.connect('attr_watch_name', self.txtName.on_object_value_changed)
        
    def unlink(self):
        super(AttributeEditor, self).unlink()
        self.attribute.disconnect(callback=self.txtName.on_object_value_changed)
        if self.sbChan is not None:
            self.attribute.disconnect(callback=self.sbChan.on_object_value_changed)
        
class CategoryEditor(EditorBase):
    topwidget_label = 'Categories'
    def init_editor(self, **kwargs):
        self.src_object = kwargs.get('src_object')
        self.category_type = None
        self.category_dict = self.DeviceSystem.Categories
        if 'attribute' in self.src_object.saved_class_name.lower():
            self.category_type = 'attribute'
            self.category_dict = self.DeviceSystem.Categories['attribute']
        elif 'device' in self.src_object.saved_class_name.lower():
            self.category_type = 'device'
            self.category_dict = self.DeviceSystem.Categories['device']
            
        self.list = widgets.TreeList(name='Categories', list_types=[str, str, bool, bool], 
                                     column_names=['id', 'Name', 'Enabled', 'cell_active'], 
                                     column_order=[1, 2], columns_editable=[1])
        self.update_list()
        self.list.connect('cell_toggled', self.on_cell_toggled)
        self.list.connect('cell_edited', self.on_cell_edited)
        self.btn_add_category = widgets.Button(label='Add Category')
        self.btn_add_category.connect('clicked', self.on_btn_add_category_clicked)
        self.list.topwidget.pack_start(self.btn_add_category)
        self.topwidget = self.list.topwidget
        
        self.DeviceSystem.connect('new_category', self.on_new_category)
        if self.src_object is not None:
            self.src_object.connect('category_update', self.on_category_update)
        
    def unlink(self):
        super(CategoryEditor, self).unlink()
        if self.src_object is not None:
            self.src_object.disconnect(callback=self.on_category_update)
            self.DeviceSystem.disconnect(callback=self.on_new_category)
            
    def update_list(self, key=None):
        if key:
            category = self.category_dict.get(key)
            self.list.update({key:[category.name, self.src_object in category.members, True]})
        else:
            for key, val in self.category_dict.iteritems():
                self.list.update({key:[val.name, self.src_object in val.members, True]})
        
    def on_btn_add_category_clicked(self, btn):
        self.DeviceSystem.add_category(type=self.category_type, name='New Category')
        
    def on_category_update(self, **kwargs):
        category = kwargs.get('category')
        state = kwargs.get('state')
        self.update_list(category.id)
        
    def on_new_category(self, **kwargs):
        category = kwargs.get('category')
        if kwargs.get('type') == self.category_type:
            self.update_list(category.id)
        
    def on_cell_toggled(self, **kwargs):
        key = kwargs.get('key')
        state = kwargs.get('state')
        category = self.category_dict.get(key)
        if state:
            self.src_object.add_category(category)
        else:
            self.src_object.remove_category(category)
            
    def on_cell_edited(self, **kwargs):
        key = kwargs.get('key')
        column = kwargs.get('column')
        value = kwargs.get('value')
        if column == 1:
            self.category_dict[key].name = value
            self.update_list(key)
            
class ParameterEditor(EditorBase):
    topwidget_label = 'Parameters'
    def init_editor(self, **kwargs):
        self.attribute = kwargs.get('attribute')
        self.list_types = [str, str, int, int]
        col_names = ['id', 'Name', 'Range Lo', 'Range Hi']
        col_order = [1, 2, 3]
        columns_editable = [1, 2, 3]
        if self.attribute.saved_class_name == 'GlobalAttribute':
            col_order = [1]
            columns_editable = [1]
        self.column_attr_map = {0:'id', 1:'name', 2:'value_min', 3:'value_max'}
        self.list = widgets.TreeList(name='Parameters', list_types=self.list_types, 
                                     column_names=col_names, column_order=col_order, 
                                     columns_editable=columns_editable)
        self.update_list()
        self.list.connect('cell_edited', self.on_cell_edited)
        self.btn_add_parameter = widgets.Button(label='Add Parameter')
        self.btn_add_parameter.connect('clicked', self.on_btn_add_parameter_clicked)
        self.list.topwidget.pack_start(self.btn_add_parameter)
        self.topwidget = self.list.topwidget
        
        self.attribute.connect('new_indexed_parameter', self.on_new_parameter)
        
    def unlink(self):
        super(ParameterEditor, self).unlink()
        self.attribute.disconnect(callback=self.on_new_parameter)
        
    def update_list(self, key=None):
        if key is not None:
            values = [getattr(self.attribute.indexed_parameters[key], self.column_attr_map[x], self.list_types[x]()) for x in [1, 2, 3]]
            self.list.update({key:values})
        else:
            for key, val in self.attribute.indexed_parameters.iteritems():
                values = [getattr(self.attribute.indexed_parameters[key], self.column_attr_map[x], self.list_types[x]()) for x in [1, 2, 3]]
                self.list.update({key:values})
                
    def on_new_parameter(self, **kwargs):
        param = kwargs.get('parameter')
        self.update_list(param.id)
        self.list.set_current_selection(key=param.id)
        
    def on_btn_add_parameter_clicked(self, btn):
        self.attribute.add_indexed_parameter(name='New Parameter')
        
    def on_cell_edited(self, **kwargs):
        key = kwargs.get('key')
        column = kwargs.get('column')
        value = kwargs.get('value')
        attr = self.column_attr_map[column]
        setattr(self.attribute.indexed_parameters[key], attr, value)
        self.update_list(key)

class UniverseEditor(EditorBase):
    topwidget_label = 'Universes'
    def init_editor(self, **kwargs):
        self.universe = kwargs.get('universe')
        self.txtName = widgets.Text(label='Name', src_object=self.universe, src_attr='name', 
                                    allow_obj_setattr=True)
        self.topwidget.pack_start(self.txtName.topwidget)
        self.patch_editor = self.make_child_editor(UniversePatchEditor, 'patch', universe=self.universe)
        self.topwidget.pack_start(self.patch_editor.topwidget, expand=True)
        
class UniversePatchEditor(EditorBase):
    topwidget_label = 'Patch View'
    def init_editor(self, **kwargs):
        self.universe = kwargs.get('universe')
        self.column_count = kwargs.get('column_count', 32)
        self.row_count = 512 / self.column_count
        self.column_types = [bool] * (self.column_count + 1)
        self.list = gtk.ListStore(*self.column_types)
        self.widget = gtk.TreeView(model=self.list)
        for x in range(self.column_count):
            cell = gtk.CellRendererToggle()
            col = gtk.TreeViewColumn(str(x+1), cell, active=x, activatable=self.column_count + 1)
            self.widget.append_column(col)
        self.topwidget.pack_start(self.widget, expand=True)
        self.update_list()
        self.DeviceSystem.connect('new_device', self.on_ds_new_device)
    def unlink(self):
        super(UniversePatchEditor, self).unlink()
        self.DeviceSystem.disconnect(callback=self.on_ds_new_device)
    def update_list(self):
        self.list.clear()
        chan = 0
        for y in range(self.row_count):
            row = []
            for x in range(self.column_count):
                chan += 1
                row.append(chan in self.DeviceSystem.patch_by_chan[self.universe.id])
            row.append(False)
            self.list.append(row)
            
    def on_ds_new_device(self, **kwargs):
        self.update_list()

class CueStackEditor(EditorBase):
    topwidget_label = 'Cue Stacks'
    def init_editor(self, **kwargs):
        self.stack = kwargs.get('stack')
        i = self.stack.current_index
        if not i:
            i = 1
        self.cue_index = i
        self.next_index = i
        hbox = widgets.HBox()
        
        list_types = [str, int, str, float]
        col_names = ['id', 'Index', 'Name', 'Fade Time']
        col_order = [1, 2, 3]
        columns_editable = [1, 2, 3]
        self.column_attr_map = {0:'id', 1:'index', 2:'name', 3:'fade_time'}
        self.list = widgets.TreeList(name='Cues', list_types=list_types, 
                                     column_names=col_names, column_order=col_order, 
                                     columns_editable=columns_editable)
        self.update_list()
        self.list.connect('cell_edited', self.on_cell_edited)
        self.list.connect('selection_changed', self.on_list_selection_changed)
        
        self.spCueIndex = widgets.SpinBtn(label='Current', src_object=self, src_attr='cue_index', 
                                          value_max=999, allow_obj_setattr=True, no_frame=True)
        self.spNextIndex = widgets.SpinBtn(label='Next', src_object=self, src_attr='next_index', 
                                           value_max=999, allow_obj_setattr=True, no_frame=True)
        self.btn_record = widgets.Button(label='Record Cue')
        self.btn_record.connect('clicked', self.on_btn_record_clicked)
        self.btn_recall = widgets.Button(label='Recall')
        self.btn_recall.connect('clicked', self.on_btn_recall_clicked)
        
        hbox2 = widgets.HBox()
        hbox2.pack_start(self.spCueIndex.topwidget)
        hbox2.pack_start(self.spNextIndex.topwidget)
        
        hbox3 = widgets.HBox()
        hbox3.pack_start(self.btn_record)
        hbox3.pack_start(self.btn_recall)
        
        self.list.topwidget.pack_start(hbox2)
        self.list.topwidget.pack_start(hbox3)
        
        hbox.pack_start(self.list.topwidget, expand=True)
        
        self.topwidget.pack_start(hbox, expand=True)
        
        self.stack.connect('new_snapshot', self.on_new_snapshot)
        self.stack.connect('current_index_changed', self.on_stack_current_index_changed)
        
    def unlink(self):
        super(CueStackEditor, self).unlink()
        self.stack.disconnect(callback=self.on_new_snapshot)
    
    def update_list(self, key=None):
        if key is not None:
            values = [getattr(self.stack.Snapshots[key], self.column_attr_map[x]) for x in [1, 2, 3]]
            self.list.update({key:values})
        else:
            for key, val in self.stack.Snapshots.iteritems():
                values = [getattr(self.stack.Snapshots[key], self.column_attr_map[x]) for x in [1, 2, 3]]
                self.list.update({key:values})
    
    def on_new_snapshot(self, **kwargs):
        key = kwargs.get('id')
        self.update_list(key)
        
    def on_stack_current_index_changed(self, **kwargs):
        current = kwargs.get('value')
        next = kwargs.get('next')
        self.cue_index = current
        self.next_index = next
        self.spCueIndex.on_object_value_changed()
        self.spNextIndex.on_object_value_changed()
    
    def on_btn_record_clicked(self, btn):
        # TODO: change the source to selected after MainController selection implemented
        self.MainController.store_to_CueStack(stack=self.stack, index=self.cue_index, source='all')
        
    def on_btn_recall_clicked(self, btn):
        if self.cue_index in self.stack.snapshots_indexed:
            self.stack.recall_snapshot(index=self.next_index)
    
    def on_cell_edited(self, **kwargs):
        key = kwargs.get('key')
        column = kwargs.get('column')
        value = kwargs.get('value')
        attr = self.column_attr_map[column]
        setattr(self.stack.Snapshots[key], attr, value)
        self.update_list()
    
    def on_list_selection_changed(self, **kwargs):
        key = kwargs.get('key')
        snapshot = self.stack.Snapshots[key]
        self.cue_index = snapshot.index
        self.next_index = snapshot.index
        self.spCueIndex.on_object_value_changed()
        self.spNextIndex.on_object_value_changed()
